/*
 * Copyright (C) 2005 - 2019 Schlichtherle IT Services.
 * All rights reserved. Use is subject to license terms.
 */
#if($licenseKeyFormat == "V4")
#set($facadePackage = "global.namespace.truelicense.v4")
#set($facadeClass = "V4")
#elseif($licenseKeyFormat == "V2/JSON")
#set($facadePackage = "global.namespace.truelicense.v2.json")
#set($facadeClass = "V2Json")
#elseif($licenseKeyFormat == "V2/XML")
#set($facadePackage = "global.namespace.truelicense.v2.xml")
#set($facadeClass = "V2Xml")
#elseif($licenseKeyFormat == "V1")
#set($facadePackage = "global.namespace.truelicense.v1")
#set($facadeClass = "V1")
#end
#set($editions = $editions.split($delimiter))
#set($editionsSize = $editions.size())
#set($hash = '#')
#set($dollar = '$')
#set($english = $field.get("java.util.Locale.ENGLISH"))
#set($preferencesType = $display.capitalize($preferencesType.toLowerCase($english)))
#macro(protection $string)$java.obfuscatedString($string).replaceAll("new global.namespace.truelicense.obfuscate.ObfuscatedString(.*)", "protection$1")#end
#macro(class $class)
#if($customClasspathScope == "runtime")
new Object() {
    Class<?> classForName() {
        try {
            return Class.forName("$class");
        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException(e);
        }
    }
}.classForName()
#else
${class.replace('$', '.')}.class
#end
#end
/* Generated from Velocity template at $date - DO NOT EDIT! */
package ${package}.keymgr;

import global.namespace.fun.io.api.Source;
import global.namespace.truelicense.api.*;
import global.namespace.truelicense.api.passwd.PasswordProtection;
import global.namespace.truelicense.core.passwd.ObfuscatedPasswordProtection;
import global.namespace.truelicense.obfuscate.*;

import $facadePackage.$facadeClass;

import java.io.InputStream;

/**
 * The enumeration of the consumer license managers for $subject license keys.
 * The managers are named like each configured edition and ordered from
 * superset to subset, including an optional free trial period (FTP).
 * Each manager is configured with the algorithms and parameters for installing,
 * loading, verifying and uninstalling license keys for the respective edition.
 * <p>
 * This class is immutable and hence trivially thread-safe.
 *
 * @author Christian Schlichtherle
 */
public enum LicenseManager implements ConsumerLicenseManager {
#foreach($edition in $editions)
#set($editionCertEntryAlias = $edition)
#evaluate("${hash}set (${dollar}editionCertEntryAlias = ${dollar}${edition}KeyEntryAlias)")
#set($editionPbePassword = $password)
#evaluate("${hash}set(${dollar}editionPbePassword = ${dollar}${edition}PbePassword)")

    $edition {
#if($editionCertEntryAlias == $edition)
#set($editionCertEntryAlias = "name()")
#else
        @Obfuscate
        private static final String CERT_ENTRY_ALIAS = "$editionCertEntryAlias";

#set($editionCertEntryAlias = "CERT_ENTRY_ALIAS")
#end
        @Override
        ConsumerLicenseManager newManager() {
            return _managementContext
                    .consumer()
#if($editionPbePassword != $parentEditionPbePassword)
                    .encryption()
#if($licenseKeyFormat == "V1" && $pbeAlgorithm != "PBEWithMD5AndDES" ||
    $licenseKeyFormat.startsWith("V2/") && $pbeAlgorithm != "PBEWithSHA1AndDESede" ||
    $licenseKeyFormat == "V4" && $pbeAlgorithm != "PBEWithHmacSHA256AndAES_128")
                        .algorithm(PBE_ALGORITHM)
#end
                        .protection(#protection($editionPbePassword))
                        .up()
#end
                    .authentication()
                        .alias($editionCertEntryAlias)
                        .loadFromResource(KEY_STORE_FILE)
                        .storeProtection(#protection($publicKeyStorePassword))
#if($licenseKeyFormat == "V1" && $publicKeyStoreType != "JKS" ||
    $licenseKeyFormat.startsWith("V2/") && $publicKeyStoreType != "JCEKS" ||
    $licenseKeyFormat == "V4" && $publicKeyStoreType != "PKCS12")
                        .storeType(KEY_STORE_TYPE)
#end
                        .up()
#if(0 < $foreach.index)
                    .parent(LicenseManager.$parentEdition)
#end
                    .storeIn${preferencesType}Preferences(Main.class) // must be a non-obfuscated class!
                    .build();
        }
    }#if($foreach.count == $editionsSize && $freeTrialPeriod == 0);#{else},#end
#set($parentEdition = $edition)
#set($parentEditionPbePassword = $editionPbePassword)
#end
#if($freeTrialPeriod != 0)
#set($ftpKeyEntryAlias = "ftp")
#evaluate("${hash}set (${dollar}ftpKeyEntryAlias = ${dollar}ftpKeyEntryAlias)")

    ftp {
#if($ftpKeyEntryAlias == "ftp")
#set($ftpKeyEntryAlias = "name()")
#else
        @Obfuscate
        private static final String KEY_ENTRY_ALIAS = "$ftpKeyEntryAlias";

#set($ftpKeyEntryAlias = "KEY_ENTRY_ALIAS")
#end
#if($ftpKeyStoreFile != $publicKeyStoreFile)
        @Obfuscate
        private static final String KEY_STORE_FILE = "$ftpKeyStoreFile";

#end
#if($licenseKeyFormat == "V1" && $ftpKeyStoreType != "JKS" ||
    $licenseKeyFormat.startsWith("V2/") && $ftpKeyStoreType != "JCEKS" ||
    $licenseKeyFormat == "V4" && $ftpKeyStoreType != "PKCS12")
        @Obfuscate
        private static final String KEY_STORE_TYPE = "$ftpKeyStoreType";

#end
        @Override
        ConsumerLicenseManager newManager() {
            return _managementContext.consumer()
                    .ftpDays($freeTrialPeriod)
                    .authentication()
                        .alias($ftpKeyEntryAlias)
#if($ftpKeyEntryPassword && $ftpKeyEntryPassword != $ftpKeyStorePassword)
                        .keyProtection(#protection($ftpKeyEntryPassword))
#end
                        .loadFromResource(KEY_STORE_FILE)
                        .storeProtection(#protection($ftpKeyStorePassword))
#if($licenseKeyFormat == "V1" && $ftpKeyStoreType != "JKS" ||
    $licenseKeyFormat.startsWith("V2/") && $ftpKeyStoreType != "JCEKS" ||
    $licenseKeyFormat == "V4" && $ftpKeyStoreType != "PKCS12")
                        .storeType(KEY_STORE_TYPE)
#end
                        .up()
                    .parent(LicenseManager.$parentEdition)
                    .storeIn${preferencesType}Preferences(#class($ftpSecretMarkerClass))
                    .build();
        }
    };
#end

    @Obfuscate
    private static final String KEY_STORE_FILE = "$publicKeyStoreFile";
#if($licenseKeyFormat == "V1" && $publicKeyStoreType != "JKS" ||
    $licenseKeyFormat.startsWith("V2/") && $publicKeyStoreType != "JCEKS" ||
    $licenseKeyFormat == "V4" && $publicKeyStoreType != "PKCS12")

    @Obfuscate
    private static final String KEY_STORE_TYPE = "$publicKeyStoreType";
#end

    @Obfuscate
    private static final String SUBJECT = "$subject";
#if($licenseKeyFormat == "V1" && $pbeAlgorithm != "PBEWithMD5AndDES" ||
    $licenseKeyFormat.startsWith("V2/") && $pbeAlgorithm != "PBEWithSHA1AndDESede" ||
    $licenseKeyFormat == "V4" && $pbeAlgorithm != "PBEWithHmacSHA256AndAES_128")

    @Obfuscate
    private static final String PBE_ALGORITHM = "$pbeAlgorithm";
#end

    private static final LicenseManagementContext _managementContext = $facadeClass
            .builder()
#if($keyMgrAuthorization != "-")
            .authorization($keyMgrAuthorization)
#end
#if($keyMgrClock != "-")
            .clock($keyMgrClock)
#end
            .subject(SUBJECT)
#if($keyMgrValidation != "-")
            .validation($keyMgrValidation)
#if($keyMgrValidationComposition != "decorate")
            .validationComposition(LicenseFunctionComposition.$keyMgrValidationComposition)
#end
#end
            .build();

    private volatile ConsumerLicenseManager _manager;

#if($freeTrialPeriod != 0)
#set($firstEdition = "ftp")
#else
#foreach($edition in $editions)
#set($firstEdition = $edition)
#end
#end
    /**
     * Returns the first license manager in the configured
     * chain-of-responsibility, which is {@link #$firstEdition}.
     * <p>
     * By default, this manager should be used to
     * {@linkplain #install() install}, {@linkplain #load() load} and
     * {@linkplain #uninstall() uninstall} license keys.
     */
    public static LicenseManager get() { return $firstEdition; }

    private ConsumerLicenseManager manager() {
        // No need to synchronize because managers are virtually stateless.
        final ConsumerLicenseManager m = _manager;
        return null != m ? m : (_manager = newManager());
    }

    abstract ConsumerLicenseManager newManager();

    private static PasswordProtection protection(long[] obfuscated) {
        return new ObfuscatedPasswordProtection(new ObfuscatedString(obfuscated));
    }

    @Override
    public LicenseManagerParameters parameters() { return manager().parameters(); }

    @Override
    public void install(Source source) throws LicenseManagementException {
        manager().install(source);
    }

    @Override
    public License load() throws LicenseManagementException {
        return manager().load();
    }

    @Override
    public void verify() throws LicenseManagementException {
        manager().verify();
    }

    @Override
    public void uninstall() throws LicenseManagementException {
        manager().uninstall();
    }
}
